﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.Practices.Domain;
using Fuse8.DomainFramework.Practices.Domain.Interfaces;
using Microsoft.Practices.Unity;

namespace Fuse8.DomainFramework.Practices.DataAccess
{
	public static class UnityContainerExtension
	{
		private static readonly Dictionary<Type, string> _connectionStrings = new Dictionary<Type, string>();
		private static IConnectionStringFactory _connectionStringFactory;

        public static void RegisterSimpleSql<TEntity, TKey>(this IUnityContainer container) where TEntity : class, IEntityObject<TKey>, new() where TKey : IComparable<TKey>, IEquatable<TKey>
        {
            RegisterSimpleSql<TEntity, TKey>(container, true, null);
        }

		public static void RegisterSimpleSql<TEntity>(this IUnityContainer container) where TEntity : class, IEntityObject<int>, new()
		{
			RegisterSimpleSql<TEntity>(container, true, null);
		}

		public static void RegisterSimpleSql<TEntity>(this IUnityContainer container, Action<PolicyConfigurator<TEntity, int>> action) where TEntity : class, IEntityObject<int>, new()
		{
			RegisterSimpleSql(container, true, action);
		}

        public static void RegisterSimpleSql<TEntity, TKey>(this IUnityContainer container, Action<PolicyConfigurator<TEntity, TKey>> action) where TEntity : class, IEntityObject<TKey>, new() where TKey : IComparable<TKey>, IEquatable<TKey>
        {
            RegisterSimpleSql(container, true, action);
        }

		public static void RegisterSimpleSql<TEntity>(this IUnityContainer container, bool supportDynamicSql = true, Action<PolicyConfigurator<TEntity, int>> action = null) where TEntity : class, IEntityObject<int>, new()
		{
			RegisterSimpleSql<TEntity, int>(container, supportDynamicSql, action);
		}

		public static void RegisterSimpleSqlNear<TEntity>(this IUnityContainer container, Func<Type, bool> typeFilter = null, bool supportDynamicSql = true) where TEntity : class, IEntityObject<int>, new()
		{
			var registerSimpleSql = ((MethodCallExpression)((Expression<Action>)(() => container.RegisterSimpleSql<TEntity>())).Body).Method.GetGenericMethodDefinition();
			var types = typeof(TEntity).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(Int32EntityObject)) && (typeFilter == null || typeFilter(x))).ToList();
			types.ForEach(x => registerSimpleSql.MakeGenericMethod(x).Invoke(null, new[] { container }));
		}

        public static void RegisterSimpleSql<TEntity, TKey>(this IUnityContainer container, bool supportDynamicSql = true, Action<PolicyConfigurator<TEntity, TKey>> action = null) where TEntity : class, IEntityObject<TKey>, new() where TKey : IComparable<TKey>, IEquatable<TKey>
        {
            var configurator = new PolicyConfigurator<TEntity, TKey>(GetConnectionString<TEntity>(container), supportDynamicSql, container);
            if (action != null)
            {
                action(configurator);
            }

            container.RegisterInstance(configurator.Repository);
            container.RegisterInstance(configurator.UnitOfWorkFactory);
            container.RegisterInstance(configurator.Mapper);
        }

		private static string GetConnectionString<TEntity>(IUnityContainer container)
		{
			var entityType = typeof (TEntity);
			if (!_connectionStrings.ContainsKey(entityType))
			{
				_connectionStrings[entityType] = GetConnectionStringFactory(container).GetConnectionString<TEntity>();
				_connectionStringFactory = container.Resolve<IConnectionStringFactory>();
			}

			return _connectionStrings[entityType];
		}

		private static IConnectionStringFactory GetConnectionStringFactory(IUnityContainer container)
		{
			return _connectionStringFactory ?? (_connectionStringFactory = container.Resolve<IConnectionStringFactory>());
		}
	}
}